#!/usr/bin/env bash

# Drop unsupported releases
drop_unsupported_releases() {

	if [[ "$1" == "all" ]]; then
		echo "Cleanup: dropping published repositories" | sudo tee -a "${DEBUGFILE}"
		BUILD_FW=()
	else
		echo "Cleanup: dropping unsupported" | sudo tee -a "${DEBUGFILE}"
		BUILD_FW=($(grep -rw config/distributions/*/support -ve 'eos' | cut -d"/" -f3))
	fi

	REPO=($(aptly publish list -config="${CONFIG}" --raw | sed "s/. //g"))
	DROP=()
	for i in "${REPO[@]}"; do
	skip=
		for j in "${BUILD_FW[@]}"; do
		[[ $i == $j ]] && { skip=1; break ; }
		done
		[[ -n $skip ]] || DROP+=("$i")
	done

	# drop
	for i in "${DROP[@]}"; do
		aptly publish drop -config="${CONFIG}" "${i}" | sudo tee -a "${DEBUGFILE}" >/dev/null
	done
}


# Display repository content
#
showall(){

		echo "Displaying common repository contents"
		aptly repo show -with-packages -config="${CONFIG}" common | tail -n +7
		for release in "${DISTROS[@]}"; do
			echo "Displaying repository contents for $release-utils"
			aptly repo show -with-packages -config="${CONFIG}" "${release}-utils" | tail -n +7
			echo "Displaying repository contents for $release-desktop"
			aptly repo show -with-packages -config="${CONFIG}" "${release}-desktop" | tail -n +7
		done
}


# Adding package
#
# @arg $1 string component
# @arg $2 string incoming folder
# @arg $3 string description
# @arg $4 input folder
#
adding_packages() {
# add deb files to repository if they are not already there
	if ! find "${4}${2}" -maxdepth 1 -type f -name "*.deb" 2> /dev/null | grep -q .; then
	return 0
	fi
	for f in "${4}${2}"/*.deb; do
			# If we have a list of last known working kernels, repack BSP files to prevent upgrade to kernel that breaks
			if [[ -f userpatches/last-known-good.map ]]; then
				PACKAGE_NAME=$(dpkg-deb -W $f | awk '{ print $1 }')
				for g in $(cat userpatches/last-known-good-kernel-pkg.map); do
					# Read values from file
					BOARD=$(echo $g | cut -d"|" -f1);
					BRANCH=$(echo $g | cut -d"|" -f2);
					LINUXFAMILY=$(echo $g | cut -d"|" -f3)
					LASTKERNEL=$(echo $g | cut -d"|" -f4);
					if [[ ${PACKAGE_NAME} == "armbian-bsp-cli-${BOARD}-${BRANCH}" ]]; then
					echo "Setting last kernel upgrade for $BOARD to linux-image-$BRANCH-$BOARD=${LASTKERNEL}"
					tempdir=$(mktemp -d)
					dpkg-deb -R $f $tempdir
					sed -i '/^Replaces:/ s/$/, linux-image-'$BRANCH'-'$LINUXFAMILY' (>> '$LASTKERNEL'), linux-dtb-'$BRANCH'-'$LINUXFAMILY' (>> '$LASTKERNEL')/' $tempdir/DEBIAN/control
					dpkg-deb -b $tempdir ${f} >/dev/null
					fi
				done
			fi
			aptly repo add -remove-files -force-replace -config="${CONFIG}" "${1}" "${f}"
	done
}


# publishing repository
#
# $1: Input folder
# $2: Output folder
# $3: Command
# $4: GPG password
# $5: jammy,sid

publishing(){

	# this repository contains packages that are the same in all releases.
	if [[ -z $(aptly repo list -config="${CONFIG}" -raw | awk '{print $(NF)}' | grep common) ]]; then
		aptly repo create -config="${CONFIG}" -distribution="common" -component="main" -comment="Armbian common packages" "common" | sudo tee -a "${DEBUGFILE}" >/dev/null
	fi

	# add packages from main folder
	adding_packages "common" "" "main" "$1"

	# create snapshot
	UNIQUE_NAME=$(date +%s)
	if [[ -n $(aptly snapshot list -config="${CONFIG}" -raw | awk '{print $(NF)}' | grep "common") ]]; then
	aptly -config="${CONFIG}" snapshot drop common | sudo tee -a "${DEBUGFILE}" >/dev/null
	fi
	aptly -config="${CONFIG}" snapshot create common from repo common | sudo tee -a "${DEBUGFILE}" >/dev/null

	# make it for all that exists. It costs little extra time
	local distributions=($(grep -rw config/distributions/*/support -ve '' | cut -d"/" -f3))
	for release in "${distributions[@]}"; do

		# create for each one
		if [[ -z $(aptly repo list -config="${CONFIG}" -raw | awk '{print $(NF)}' | grep "${release}-utils") ]]; then
		aptly repo create -config="${CONFIG}" -component="${release}-utils" -distribution="${release}" -comment="Armbian ${release}-utils repository" "${release}-utils" | sudo tee -a "${DEBUGFILE}" >/dev/null
		fi
		if [[ -z $(aptly repo list -config="${CONFIG}" -raw | awk '{print $(NF)}' | grep "${release}-desktop") ]]; then
		aptly repo create -config="${CONFIG}" -component="${release}-desktop" -distribution="${release}" -comment="Armbian ma${release}-desktop repository" "${release}-desktop" | sudo tee -a "${DEBUGFILE}" >/dev/null
		fi

		adding_packages "${release}-utils" "/extra/${release}-utils" "release utils" "$1"
		adding_packages "${release}-desktop" "/extra/${release}-desktop" "release desktop" "$1"

		# drop release utils snapshot
		if [[ -n $(aptly snapshot list -config="${CONFIG}" -raw | awk '{print $(NF)}' | grep "${release}-utils") ]]; then
			aptly -config="${CONFIG}" snapshot drop ${release}-utils | sudo tee -a "${DEBUGFILE}" 2>/dev/null
		fi
		# drop release desktop snapshot
		if [[ -n $(aptly snapshot list -config="${CONFIG}" -raw | awk '{print $(NF)}' | grep "${release}-desktop") ]]; then
			aptly -config="${CONFIG}" snapshot drop ${release}-desktop | sudo tee -a "${DEBUGFILE}" 2>/dev/null
		fi

		aptly -config="${CONFIG}" snapshot create ${release}-utils from repo ${release}-utils | sudo tee -a "${DEBUGFILE}" >/dev/null
		aptly -config="${CONFIG}" snapshot create ${release}-desktop from repo ${release}-desktop | sudo tee -a "${DEBUGFILE}" >/dev/null

		echo "Publishing $release"

		aptly publish \
			-skip-signing \
			-architectures="armhf,arm64,amd64,riscv64,i386,loong64,all" \
			-passphrase="${4}" \
			-origin="Armbian" \
			-label="Armbian" \
			-config="${CONFIG}" \
			-component=main,${release}-utils,${release}-desktop \
			-distribution="${release}" snapshot common ${release}-utils ${release}-desktop > /dev/null
done
# cleanup
aptly db cleanup -config="${CONFIG}"
# key
mkdir -p "${2}"/public/
cp config/armbian.key "${2}"/public/
# write repo sync control file
sudo date +%s > ${2}/public/control
# display what we have
showall
}


# Sign repository Release files in the given output folder using provided GPG keys
# $1: Output folder path
# $@: GPG key IDs to use for signing
signing() {
    local output_folder="$1"
    shift
    local gpg_keys=("$@")

    if [[ ${#gpg_keys[@]} -eq 0 ]]; then
        echo "No GPG keys provided for signing." >&2
        return 1
    fi

    local gpg_params=("--yes" "--armor")
    for key in "${gpg_keys[@]}"; do
        if ! gpg --list-secret-keys "$key" >/dev/null 2>&1; then
            echo "Warning: GPG key $key not found on this system." >&2
			continue
        fi
        gpg_params+=("-u" "$key")
    done

    find "$output_folder/public/dists" -type f -name Release | while read -r release_file; do
        local distro_path
        distro_path="$(dirname "$release_file")"
        echo "Signing release at: $distro_path" | sudo tee -a "$DEBUGFILE"
        gpg "${gpg_params[@]}" --clear-sign -o "$distro_path/InRelease" "$release_file"
        gpg "${gpg_params[@]}" --detach-sign -o "$distro_path/Release.gpg" "$release_file"
    done
}


#
# $1: Input folder
# $2: Output folder
# $3: Command
# $4: GPG password
# $5: jammy,sid
# $6: list of packages to delete
repo-manipulate() {

# read comma delimited distros into array
IFS=', ' read -r -a DISTROS <<< "$5"

case $3 in

	serve)

		sudo aptly serve -listen=$(ip -f inet addr | grep -Po 'inet \K[\d.]+' | grep -v 127.0.0.1 | head -1):80 -config="${CONFIG}"
		return 0
	;;

	html)
		cat tools/repository/header.html
		for release in "${DISTROS[@]}"; do
		echo "<thead><tr><td colspan=3><h2>$release</h2></tr><tr><th>Main</th><th>Utils</th><th>Desktop</th></tr></thead>"
		echo "<tbody><tr><td width=33% valing=top>"
		aptly repo show -with-packages -config="${CONFIG}" "${release}-utils" | tail -n +7 | sed 's/.*/&<br>/'
		echo "</td><td width=33% valign=top>" | sudo tee -a ${filename}
		aptly repo show -with-packages -config="${CONFIG}" "${release}-desktop" | tail -n +7 | sed 's/.*/&<br>/'
		echo "</td></tr></tbody>"
		done
		cat tools/repository/footer.html
		return 0
	;;

	delete)
			echo "Deleting $6 from common"
			aptly -config="${CONFIG}" repo remove common "$6"
			for release in "${DISTROS[@]}"; do
				echo "Deleting $6 from $release-utils"
				aptly -config="${CONFIG}" repo remove "${release}-utils" "$6"
				echo "Deleting $6 from $release-desktop"
				aptly -config="${CONFIG}" repo remove "${release}-desktop" "$6"
			done
			return 0
		;;

	show)

		showall
		return 0

	;;

	unique)
		# which package should be removed from all repositories
		IFS=$'\n'
		while true; do
			LIST=()
			LIST+=($(aptly repo show -with-packages -config="${CONFIG}" common | tail -n +7))
			for release in "${DISTROS[@]}"; do
				LIST+=($(aptly repo show -with-packages -config="${CONFIG}" "${release}-utils" | tail -n +7))
				LIST+=($(aptly repo show -with-packages -config="${CONFIG}" "${release}-desktop" | tail -n +7))
			done
			LIST=($(echo "${LIST[@]}" | tr ' ' '\n' | sort -u))
			new_list=()
			# create a human readable menu
			for ((n = 0; n < $((${#LIST[@]})); n++)); do
				new_list+=("${LIST[$n]}")
				new_list+=("")
			done
			LIST=("${new_list[@]}")
			LIST_LENGTH=$((${#LIST[@]} / 2))
			exec 3>&1
			TARGET_VERSION=$(dialog --cancel-label "Cancel" --backtitle "BACKTITLE" --no-collapse --title \
			"Remove packages from repositories" --clear --menu "Delete" $((9 + LIST_LENGTH)) 82 65 "${LIST[@]}" 2>&1 1>&3)
			exitstatus=$?
			exec 3>&-
			if [[ $exitstatus -eq 0 ]]; then
				aptly repo remove -config="${CONFIG}" "common" "$TARGET_VERSION"
				for release in "${DISTROS[@]}"; do
					aptly repo remove -config="${CONFIG}" "${release}-utils" "$TARGET_VERSION"
					aptly repo remove -config="${CONFIG}" "${release}-desktop" "$TARGET_VERSION"
				done
			else
				return 1
			fi
			aptly db cleanup -config="${CONFIG}" > /dev/null 2>&1
			# remove empty folders
			find $2/public -type d -empty -print -exec rm -rf {} \;
		done
		;;

	update)
		# remove old releases from publishing
		drop_unsupported_releases "all"
		publishing "$1" "$2" "$3" "$4" "$5"
		# use the signing function to sign the repository
		signing "$2" "DF00FAF1C577104B50BF1D0093D6889F9F0E78D5" "8CFA83D13EB2181EEF5843E41EB30FAF236099FE"
		;;

	*)
		echo -e "Unknown command"
		return 1
		;;
esac
}


# defaults
input="output/debs-beta"
output="output/repository"
command="show"
releases=$(grep -rw config/distributions/*/support -ve 'eos' | cut -d"/" -f3 | xargs | sed -e 's/ /,/g')

help()
{
echo "Armbian wrapper for Aptly v1.0

(c) Igor Pecovnik, igor@armbian.com

License: (MIT) <https://mit-license.org/>

Usage: $0 [ -short | --long ]

-h --help displays this
-i --input [input folder]
-o --output [output folder]
-p --password [GPG password]
-r --repository [jammy,sid,bullseye,...]
-l --list [\"Name (% linux*)|armbian-config\"]
-c --command

          [show] displays packages in each repository
          [sign] sign repository
          [html] displays packages in each repository in html form
          [serve] serve repository - useful for local diagnostics
          [unique] manually select which package should be removed from all repositories
          [update] search for packages in input folder
          [delete] delete package from -l LIST of packages
	"
    exit 2
}

SHORT=i:,l:,o:,c:,p:,r:,h
LONG=input:,list:,output:,command:,password:,releases:,help
OPTS=$(getopt -a -n repo --options $SHORT --longoptions $LONG -- "$@")
DEBUGFILE="/var/log/repo-management.log"

VALID_ARGUMENTS=$# # Returns the count of arguments that are in short or long options

eval set -- "$OPTS"

while :
do
  case "$1" in
    -i | --input )
      input="$2"
      shift 2
      ;;
    -o | --output )
      output="$2"
      shift 2
      ;;
    -c | --command )
      command="$2"
      shift 2
      ;;
    -p | --password )
      password="$2"
      shift 2
      ;;
    -r | --releases )
      releases="$2"
      shift 2
      ;;
    -l | --list )
      list="$2"
      shift 2
      ;;
    -h | --help)
      help
      ;;
    --)
      shift;
      break
      ;;
    *)
      echo "Unexpected option: $1"
      help
      ;;
  esac
done

# redefine output folder in Aptly
TempDir="$(mktemp -d || exit 1)"
sed 's|"rootDir": ".*"|"rootDir": "'$output'"|g' tools/repository/aptly.conf > "${TempDir}"/aptly.conf
CONFIG="${TempDir}/aptly.conf"

# main
repo-manipulate "$input" "$output" "$command" "$password" "$releases" "$list"
RETURN=$?
exit $RETURN
